package md.specialEqp.inspect;

import lombok.*;
import md.cm.geography.Adminunit;
import md.cm.unit.Unit;
import md.specialEqp.BusinessCat_Enum;
import md.specialEqp.Eqp;
import md.system.User;
import org.fjsei.yewu.filter.Uunode;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

import static jakarta.persistence.FetchType.LAZY;


/**协议泛型申请单：等价于是一个zeebe流程引擎的配套配置和跟踪实体(和Report.stm类比:流转每一份分项报告)。
 * 前端页面就得依据申请单对应实体@Entity模型不同分别需要设置一个菜单入口主页面，独立的查询该实体模型的申请单页面和独立对应的操作历史跟踪，申请类型不同编辑器字段数据名目也不同啊。
 * 某一类型申请单：目前用于：1:协议或业务合同：用于溯源和备案，兼有审核跟踪。
 * 2:兼容挪用变身成其它类似申请，字段语义上可以兼容本实体模型的一类型的申请单。
 * 类比在旧平台的新委托协议受理菜单的技术服务协议，窗口综合业务受理菜单的检验检测、监检协议书。
 * 和流程引擎配套的来完成一组功能。
 * 【各种各样的】类型的申请单如何合并泛型化：统一到一个表或实体模型类，拆解数据和审核通过后的需要被授权执行代码动作，流程引擎，代码数据分解。
 * 嵌套的子{孙}流程：接收申请可签署协议--》{ 协议审核--》{ 收费金额计算} }+ 签字生效受理完成[没达成撤销的需要处理已经预生成的任务删除?]。
 * */

@Getter
@Setter
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Builder(toBuilder=true)
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Fast")
public class Agreement implements Uunode {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    /**协议类型： 前端掌握的字典类型编码:比较固定编码。
     * 用区分到底aux有那些数据字段，前端协议内容展示，zeebe流程处理步骤的执行都可能受到pttype参数不同影响。
     */
    private String  pttype;

    /** 协议编号: 发起人维护。 协议书没有打印写明的也能隐藏设置一个自己认可的唯一标识。
     * 协议编号不唯一：发起协议的个人敲定的，后陆续审批人可能要求再做修改的：甲方和乙方认可编号，当前检验的特检院为主来管理编码规则。
     * 主动报检途径的可由特检院受理人来补录？
     * 旧平台SDN过程报检，需要流转接续多次，紧密耦合?。提交用户业务受理（协议签订完毕/让对方确认）。
     * 报检平台SDN用户主动申请过来的：需要分派到不同的地区部门也即按照业务大类自动划转流转给哪个部门的哪个受理人员-zeebe要求用户任务指定到明确User。
     * 具体特检院都有自己的入口人员：SDN全部指定到窗口特定办事员，然后窗口转移交到下一步zeebe流程人员。内部人直接提交办理人员，报检SDN的人让窗口人员转交办。
     * 窗口人员:zeebe用户任务有问题？ 办事人请假的会阻碍流程进度，应该把流程人员屏蔽，还得把已经生成的流程实例转交其它人员UserTask退回或交接移交。
     * 检验检测、监检协议书 协议编号：QZ-03730-2020(J)
     * 技术服务协议 编号
     */
    private String  ptno;

    /** 特检院;    缺省 =福建省特检院。
     * 支持多个检验机构都能使用本平台出报告和业务流程的对所有检验机构都能共同支持。
     * */
    @NotNull
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    private Unit  ispu;

    /**协议协商进度：zeebe内部状态主要参考preNode取值+preMan。
     *  流程进度，表征意义，不是和zeebe引擎的BPMN节点一一对应的。
     * 申请单状态：实际是zeebe流程运行环节映射值。其实主要的状态值已经划归流程引擎内置了，引擎运行到了哪一个环节就是某一状态值(不一定对本实体可感知的)。
     * 协议运行状态字。 有些取值仅仅只读反馈用途，不可以当做操作的先决条件或逻辑依据。
     监检 PROTOCOL_STATUS状态，-1,作废，0：申报，1：待审核，2:审核通过，待导出协议等操作，3：提交用户业务受理（协议签订完毕）,4:业务受理申请,5：业务受理终结
     委托 PROTOCOL_STATUS协议状态，-1,作废，0：申报，1：待审核，3：审核通过，提交用户业务受理（协议签订完毕）,4:业务受理申请,5：业务受理终结
     zeebe流程图上节点和这个没有绝对对应关系，这个状态代表大概的关卡也就是流转审批进度。
     不加@Enumerated(EnumType.STRING)默认Enum在数据库存储是int这个可不能随意修改_Enum顺序值。
     */
    @Builder.Default
    @Enumerated(EnumType.STRING)
    private ProtocolSta_Enum  status =ProtocolSta_Enum.INIT;

    /**关联设备, 多台；
     * 安装监检，管道的如何初始化设备台账：必须先登记关键主键字段信息，其它的允许补齐其它参数字段？影响到计费必须首先录入到台账，
     * 未注册的监察平台还没有的设备:报检用户申报的新设备也得初始化台账后再来走这个协议计费计算依据的除外，或者收费计算按量计价没事前明确最终金额数值的也是允许。
     * 正常协议受理完毕不能修改设备列表了不再生成任务新Task，框架协议承包形式的大合同情况？才允许在协议受理结束之后还能够调整关联设备列表。
     * 新设备正常由检验人员或窗口受理人员新添加Eqp。 水质或制造监检或其它未能进入设备台账管控的非常规业务的情况没有这个关联信息。
    * */
    @ManyToMany
    @JoinTable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL,region ="Fast")
    private List<Eqp>  devs;

    /**申请单位，签订业务协议的单位: 施工单位。 不一定等于设备台账的使用单位。
     * 报检SDN用户归属单位必须是在Unit存在的{使用单位}。否则走只能代理方式：委托电梯公司承包，关联设备才需要使用单位的。
     * 其它未能进入设备台账管控的如果不在Unit的服务对象：也得首先建立一个新Unit才行;Unit可能特检院就能添加不一定要监察平台映射单位。
     */
    @ManyToOne(fetch= FetchType.LAZY)
    @JoinColumn
    private Unit  servu;

    /**创建人： 初始的平台用户也就是发起申请的User。
     * 用户，协议发起人，或者用户的代理人代表。窗口现场签署协议的如何=》签字身份证明？当场验证有效性。
     * 最后审核人审批通过还要转回到这里让crman确认和进行签名，协议才是真正受理结束。Eqp准备好新设备的也得初始化设备。
     * 有编辑 该申请单 权限的编制人。
     * 不是同一个后端服务器 如何创建Agreement流程引擎还得标识用户ID唯一性；数据模型可已经过zeebe打包传递,代理模式代为创建新Agreement实体:crman也填上对应人员?。
     * 申请人。流程发起人。
     * 本实体的初始创建者可能是特检院检验窗口人员 或检验员 或使用单位用户 或者维保单位承包商。
     * “监检协议”可能停顿拖沓太久的情况：crman最好是有User账户的利害关系人==用户，检验负责人，不然时间周期太长的很可能遗忘？或者每个初始阶段状态也要定期超期检测的异常维护提醒模式。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    private User crman;

    /**窗口工作人员 :采用检验单位的首位接待或值班负责人员主动查询接收受理模式（分配控制逻辑），随后才是推入zeebe流程引擎继续下面的流程。
     * 负责和用户协调 促成协议书的最终签署。初步审查SDN资料，也可以修改部分协议的字段。
     * 特检院首次接收申请单人员也即分配目标业务部门的窗口工作人员，
     * 这一步没必要用zeebe流程牵涉入：让窗口User自个查询列表 和日常经常性事务受理模式即可，半个小时有人能够响应接受业务。审核环节长期不动的要能撤回。
     * 窗口受理人员，特检院窗口人员, 人工受理，检验人员受理，电梯公司代理申请形式的；
     * 只要一天之内没请假有能够上班或网络形式值班的都算 允许正常受理人。
     * 不管哪个来源，全部都首先进入受理人员接单节点：zeebe流程保持一致性和简洁性。
     * 申请人，创建人：报检平台过来用户发起的。
     * 受理部门 :
     * 客户SDN代理提请申请，检验员代为申请提交，窗口人员直接受理提交。##统一到窗口人员受理是必须经过的环节。
     * SDN报检用户直接提交给某个窗口人，这时刻promoter直接就是正在当班的人员（获取当班人员）。窗口人进一步提请审核人。
     * SDN报检用户需要这个promoter;
     * 检验人员或窗口人工受理协议的情况就不需要promoter字段的：zeebe流程跳过本环节。 =promoter=crman， 或者让crman==被代理的客户User?必须有账户User不行只能作罢。
     * 【流程】zeebe开始起点：第一节点=人工分配业务部门的审核人。审核人退回：可直接退回crman账户，让编制申请人再次提交但是这一次直接转审核人不需要经过受理人员环节？更换审核人员吗直接退回受理人员。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    private User promoter;

    /**谁负责审核，审核部门： 若过程报检的可能会让zeebe流程往返于两个节点之间。负责资料审查和业务可完成性检查。
     * 负责审查 计费金额，协议最终一关也得审查，审核人提请给最后责任人。
     * 下一步谁负责的？针对受理环节。
     * 审核部门：
     * 负责Task的生成，事前审查。
     * CHECK审核 阶段通过后：原则上协议书内容不能再修改了。再改还得提交后续增补审批。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    private User auditor;

    /**最后责任人：协议实际工作落实人员,  检验部门：谁负责派工,分派Task任务。负责干活和协议收费终结等后续事情。
     * 负责计费和任务生成。最终检验部门的具体责任人。Task预生成预先计算收费，后期若协议并没有真正受理成功要取消的话，Task关联的协议申请单，顺带删除预生成的Task关联检验Detail。
     * 下一步谁负责的？针对工作任务环节。
     * 检验科室：
     * 负责真正的业务调度以及最后的协议上的完成结束步骤。
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    private User dispatcher;

    /**是委托的吗, 牵涉收费管控。法定的收费不能变动，委托的可以折扣？
     * 特殊任务和报告可能根本不需要关联发票的，不需要单独做收费；在签订协议上总金额/按年结算；根据业务和单位来审核=排除收费管控环节。
     * */
    private Boolean  entrust;

    /**OPE_TYPE 业务大类， 有的还需要配合REP_TYPE来区分 更详细的工作内容模板。
     * 这里的bsType/entrust优先覆盖Isp.bsType/entrust; 【注意】一个任务bsType/entrust只能保持唯一性。
     * 业务类型 OPE_TYPE：统计？模板？ TB_DICT_OPETYPE似乎没内涵36个压缩到实际28个/委托法定=
     * 法定还是委托的 是 监管归属属性，收入统计归属/报告制作差异。
     * 承压还是机电的 是 设备属性，部门归属，角色大尺度划线权限，发票区分财务开票点。
     * 同一个Task底下的Isp的业务类型OPE_TYPE必须相同，都准备派工给同一个责任人的。
     * "3", 全面: "358230" 今年条数含作废的;
     * "603",委托的: "1624"}
     * "8", 外部: "93300"}
     * "608",委托的: "720"}
     * "13"其它: "63104"} 主要是"700003"安全阀离线校验,"58416条; "700001"安全阀在线校验, "4573条;"EQP_SORT":"FZ00"EQP_VART":"7310",没有进入Eqp？
     * "2"安装监检: "33340"}
     * "1"制造监检: "8747"}
     * "16"首检: "7943"}
     * "19"热效率测试: "5920"}
     * "11"检测: "5325"}
     * 业务类型除了3，8，603，608之外的，很少集中生成任务？:
     老旧电梯评估"618", 9: "45"} 年初大批生成的:评估.
     热效率测试="19", 9: "5160"} 年初批量手写命令加: 710006 710029 到了日期遇见停用不要做了。
     其它=13 "13", 9: "4955"} 随时加 700003 700001 400041控管理系统定/法定{检验时附带生成任务} 400040
     安装监检"2", 9: "3080"} 监检TASK_DATE没约束力甚至比CREATE_DATE还早，管道拖延时间很长
     制造监检"1", 9: "942"} 200001压力容器产品安全性能监督检验 232001 800017 100001 200005；TASK_DATE没约束力 FACT_PRIC=50 多份Task/Isp/REPORT_COD
     首检"16", 9: "841"} 500023叉车首次检验 400045 500024 500025；TASK_DATE没约束力; INVC_COD复检没变。
     检测"11", 9: "321"}  710001法定/工业锅炉水质检验 710002锅炉水汽质量检测｛检验部门是节能中心不是预分配的电锅中心｝人工搜索满足条件的再做添加;
     */
    @Enumerated
    private BusinessCat_Enum bsType;

    /**业务作业的地理区域。   所属区域：
     * */
    @ManyToOne(fetch= FetchType.LAZY)
    private Adminunit ad;

    /**转款单位：可能不是服务单位 可能没有现成的Unit可对应的？ 改用文本串来声明。
     * 对接上 账务系统或发票审核的。  #一致性确保：转款单位+缴款单编码+金额+时间范围+备注? 的校对：谁为啥交钱。
     * 转款联系人：   转款联系电话;
     * 发票对应上的？
     */
    private String  transferor;         //允许不是Unit的也能顺利转账来。

    /**一切都完成# 预定的服务日期：(任务截止日 | null)不是开始日的； 生成的Task截止工作日期。
     * 截止完成日期；
     * 这个日期过后，假如还没有业务上结束本协议书的如何处理？ 任务和协议和收费归档都完成了；终结。
     * "检验检测、监检协议书"对本日期没有要求，需要依据流程时间推进，配合status状态人工控制或者crman负责推进。
     * */
    private LocalDate  complDate;

    /**必须收费金额; 本协议的真正业务上结束操作的前置要求：必须满足全额地回款。
     这个和本协议说关联的发票直接相关。
     审核阶段没有明确收费金额的？>=0？这种情况的业务结束按钮执行人的角色设置权限级别要求高点。
     结束本协议的审查：收到钱协议可完结等待一段时间过期后删除。
     最后dispatcher负责业务结束操作，时间跨度真的太久远了？需更换成新的负责人 & zeebe流程实例重置吗。
     ？多退少补的约定形式？ 协议生效可协商收费结算。
     管道单元需要等待责任人派工命令才会有报告初始化才能开始计费，提前派工选择单元才能精准计费。
     dispatchTaskTo 已经派工出来的但是其附属协议还没有生效的，可以设置taskDate=超长期限协议之后的。dispatchTaskTo允许多次执行。
     点“进入报告编制”后才有report.data有效数据。没有检验也能提前编制检验报告原始记录部分数据：管道单元对于协议附属任务，需要提前敲定管道单元计费数据。
     允许提前编制Report.data{}：同时选择需要本次作业的全部的管道单元，一次选定并排序页面报告区域位置,同时设置该次Isp/Detail的商定：最终的管道单元选择集合。允许提前去精准计费。
     */
    private Double  charge;

    /**协议签订日期，注意：协议签订以后这个日期求不要做修改了。
     * 最新修改时间。新创建申请单=最新的。
     *未生效的协议等同=申请单的修改时间， 生成的时间。
     * */
    private LocalDateTime  mdtime;

    /**【权衡考虑】表单或允许走非结构化形式的更多的前端自定义扩充的字段；非结构化的优点，弱点。
     * 兼容很多不同的协议内嵌编辑内容字段，
     * 可惜JSON非结构化的弊端需要考虑(DB上做过滤搜索/关联约束)，不行只能抽出来放入@Entity模型中。
     * 非实时定期一年做一回的统计需求还好说：json可以提取走大数据统计模式。实时过滤或统计有硬性需求的字段就不能塞入json中。JPA实体关系关联也是没法graphQL内省。
     * 类似zeebe的表单概念form;
     * 前端保存需要转换 data:  JSON.stringify(storage || source) ,提取也要转const  svp = JSON.parse(getDeviceSelf?.svp || '{}');
     * 【怪】这里没保存监检协议书的项目名称！后面或许会有“中止监检通知书”却需要给定项目名称：可能前面协议签署时刻不关注项目名或者名字楼盘工程名都还没想好的吗。
     */
    @Lob
    @Basic(fetch=LAZY)
    @Column( columnDefinition="jsonb")
    private String  aux;

    /* 转换输出给前端的graphQL类型，从原生的User改成UserBase。
     * graphQL接口中间件自动优先使用返回类型一样的函数作为模型的字段映射：就是实际这里liabler()替换掉getLiabler()，但是对java后端其它代码没有影响。
     * graphQL模型文件定义改成liabler: UserBase会导致.SchemaClassScannerError: Two different classes used for type UserBase:错,需要换个壳给graphQL。
     * 前端越权内省字段liabler { photoURL } 后端就会报错了：.GraphQL  : Query failed to validate :#无法泄露UserBase中没有发布的字段,User才有photoURL字段。
     * */
//    public UserBase liabler(){
//        return this.liabler;
//    }

    /**是否关联任务都已经完成收费缴款工作？
     * 要和 charge 金额进行会计校对。
     * */
    private Boolean  feeOk;

    /*审核人备忘录: 针对性保存审核人笔记，不在协议书体现。
     */
    //private String  ckMemo;

    /*流程的 上一个环节处理人。
     * 没有通过 Operate 网站查询流程运行节点的最新视图，无法判定上一个节点干什么的，谁啊。
     * */
//    @ManyToOne(fetch= FetchType.LAZY)
//    private User preMan;

    /*流程上一个环节=入口路径标识， 中文名字。
     * 没有通过 厂家提供的Operate 网站查询流程运行节点的最新视图，无法判定上一个节点干什么的？
     * 子流程可能并发？？最新触发的代码节点, 交叉影响的。
     * 初始化='';
     * 前端针对性显示：有些特别环节需要区分前一个节点的ID差异。
     * 光光依赖实体.status不能表达流程进度条啊：zeebe状态尽量不外露给实体：表示一个流程定义完成一件事情切换最终完成还是取消状态。
     * 而奇怪的是这preNode：透露出zeebe内部节点运行位置线路和状态。也方便反过来追查(不通过Operate网页去查的方式)。
     * */
//    private String  preNode;

    /**关联的任务，允许多个任务单，
     * 分期分批 的业务进度
     * */
    @OneToMany(mappedBy="agreement")
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL,region ="Fast")
    private List<Task> tasks;

    /**流程实例ID 流程引擎Zeebe返回的关键字。
     * 同一个申请单有可能对prId也会变更的。
     * 流程引擎上面部署的ServiceTask代码和状态分支逻辑要配套地和本申请单实体以及关联的状态字段来综合决定下一步操作。
     * 逻辑状态的在zeebe上肢解和配合。
     * 不一定都是同一个流程Process Id实例, 编制阶段的反复要求提交材料和修改流程？审核(反复要求提交材料)任务阶段流程，后续增补审批流程。
     * prId还可能还会改变啊。 zeebe给的ProcessInstanceKey是long类型。
     * */
    private Long prId;
}




/*CRDB数据库实际上多对多关联的中间表，完全不用单独新建ID作为主键的，可利用复合主键当做PRIMARY KEY,不影响JPA的操作。
    --#ManyToMany中间表修改: 22.2.3版本不需要BEGIN;COMMIT;drop column rowid;简化掉了
    ALTER TABLE public.agreement_eqp ADD COLUMN id UUID NOT NULL DEFAULT gen_random_uuid();
    ALTER TABLE public.agreement_eqp ALTER PRIMARY KEY USING COLUMNS (id);
CREATE INDEX agreement_eqp_agreement_id_index ON public.agreement_eqp USING btree (agreement_id ASC);
CREATE INDEX agreement_eqp_devs_id_index ON public.agreement_eqp USING btree (devs_id ASC);
原始的， Hibernate自动生成中间表如下：  CockroachDB201Dialect方言的。
CREATE TABLE public.agreement_eqp (
  agreement_id UUID NOT NULL,
  devs_id UUID NOT NULL,
  rowid INT8 NOT VISIBLE NOT NULL DEFAULT unique_rowid(),
  CONSTRAINT agreement_eqp_pkey PRIMARY KEY (rowid ASC),
  CONSTRAINT fk9feu45f8lo88phn79m582ibkr FOREIGN KEY (devs_id) REFERENCES public.eqp(id),
  CONSTRAINT fkj8f6bl1v8x1h0gpafqoatdntj FOREIGN KEY (agreement_id) REFERENCES public.agreement(id)
) 需要人工改造如下的 CRDB 22.2.3版本：
* */


/* @数据库修改脚本：
    --中间表修改: 22.2.3版本的小强数据库，旧的关联表完全删除后Hibernate刚刚自动新建的。复合主键的两个字段注意有顺序的！
    ALTER TABLE public.agreement_eqp ALTER PRIMARY KEY USING COLUMNS (agreement_id, devs_id);
    CREATE INDEX agreement_eqp_devs_id_index ON public.agreement_eqp USING btree (devs_id ASC);
* */